package vn.wallet.app.managedBean;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;

import org.apache.log4j.Logger;
import org.primefaces.context.RequestContext;
import org.primefaces.event.ItemSelectEvent;
import org.primefaces.event.TabChangeEvent;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SelectableDataModel;
import org.primefaces.model.SortOrder;
import org.primefaces.model.chart.PieChartModel;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import vn.wallet.app.domain.Budget;
import vn.wallet.app.domain.Payment;
import vn.wallet.app.domain.Scheduler;
import vn.wallet.app.helper.AppUtils;
import vn.wallet.app.helper.Converter;
import vn.wallet.app.helper.PageUtils;
import vn.wallet.app.service.IBudgetService;

@ManagedBean(name = "budgetBean")
@ViewScoped
public class BudgetBean extends DefaultBean implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private final Logger log = Logger.getLogger(this.getClass());

	@ManagedProperty(value = "#{budgetService}")
	private IBudgetService budgetService;

	// --------------beans properties
	private final boolean isProjectedEnabled = false;
	private boolean isAddNew;
	private boolean isUpdate;
	private boolean updateMultitple = false;
	private int isDisabledAll = -1;
	private LazyDataModel<Budget> lazyModel;
	private List<Budget> bkBudgetTable;
	private Budget[] selectedRows;
	private Budget customBudget;
	private PieChartModel budget_overview_model;
	private PieChartModel budget_details_chart;
	private Budget budgetItemSelected;
	private boolean showBudgetDetails = false;

	private final String Dialog_Add_New_Header = AppUtils.getResource("wallet.app.budget.dialog.addNew.header");
	private final String Dialog_Update_Header = AppUtils.getResource("wallet.app.budget.dialog.update.header");
	private final String Dialog_Update_Multi_Header = AppUtils.getResource("wallet.app.budget.dialog.updateMultiBudget.header");

	// -------------------properties

	public BudgetBean() {
		initNewBudget();
		initChart();
	}

	@Override
	@PostConstruct
	public void prepareData() {
		lazyModel = new LazyBudgetDataModel();
	}

	@Override
	protected void setPageTitle(String str) {
		super.siteConfig.setSiteTitle("Budget Bean");
	}

	public void onTabChange(TabChangeEvent event) {
		System.out.println("onTabChange");
	}

	/**
	 * if modeID = 0: Create new budget for dialog form<br/>
	 * if modeID = 1: init property of selected budget to dialog form<br/>
	 * 
	 * @param modeID
	 */
	public void prepareItem(long modeID) {
		System.out.println("prepareItem");
		if (modeID == 0) { // new item
			isAddNew = true;
			initNewBudget();

			RequestContext.getCurrentInstance().execute("changeDialogHeader('" + Dialog_Add_New_Header + "')");
		} else if (modeID == 1) { // edit mode
			isUpdate = true;
			if (selectedRows.length == 0) {
				RequestContext.getCurrentInstance().execute("alert('Please choose a budget to edit');");
			} else if (selectedRows.length == 1) {
				customBudget = selectedRows[0];

				RequestContext.getCurrentInstance().execute("changeDialogHeader('" + Dialog_Update_Header + "')");
			} else { // edit multi budget
				setUpdateMultitple(true);
				setIsDisabledAll(-1);
				initNewBudget();

				RequestContext.getCurrentInstance().execute("changeDialogHeader('" + Dialog_Update_Multi_Header + "')");
			}
		}
	}

	public void storeItem() {
		// validate budget
		if (validBudget()) {
			if (isUpdate) {
				updateItem();
			} else if (isAddNew) {
				saveItem();
			}

			RequestContext.getCurrentInstance().update("form_main_top:data_table");
			refreshBudgetOverviewChart();
			RequestContext.getCurrentInstance().update("budget_chart");
		} else {
			System.out.println("validBudget failed");
			RequestContext.getCurrentInstance().addCallbackParam("validationFailed", true);
		}
	}

	@Override
	public void saveItem() {
		try {
			budgetService.save(customBudget);
			PageUtils.logSuccess(PageUtils.FUNC_NAME.ADD, "");
			log.info("Added new budget: " + customBudget.getName());

			isAddNew = false;
			initNewBudget();
		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
			PageUtils.logFailed(PageUtils.FUNC_NAME.ADD, "");
		}
	}

	@Override
	public void updateItem() {
		try {
			for (Budget budget : selectedRows) {
				budget.setLastModified(new Date());

				// for multi update
				if (getIsDisabledAll() == 0) {
					budget.setStatus(false);
				} else if (getIsDisabledAll() == 1) {
					budget.setStatus(true);
				}

				if (isUpdateMultitple()) {
					if (customBudget.getScheduler().getBeginOn() != null) {
						budget.getScheduler().setBeginOn(customBudget.getScheduler().getBeginOn());
					}
					if (customBudget.getScheduler().getFinishOn() != null) {
						budget.getScheduler().setFinishOn(customBudget.getScheduler().getFinishOn());
					}
				}
			}

			budgetService.update(selectedRows);
			PageUtils.logSuccess(PageUtils.FUNC_NAME.EDIT, "");
			log.info("Updated budget");

			isUpdate = false;
			initNewBudget();
			setUpdateMultitple(false);
		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
			PageUtils.logFailed(PageUtils.FUNC_NAME.EDIT, "");
		}
	}

	@Override
	public void deleteItem() {
		try {
			budgetService.delete(selectedRows);
			PageUtils.logSuccess(PageUtils.FUNC_NAME.DELETE, "");

			log.info("Deleted budget: " + customBudget.getName());
			initNewBudget();
		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
			PageUtils.logFailed(PageUtils.FUNC_NAME.DELETE, "");
		}
	}

	private boolean validBudget() {
		boolean passed = true;

		// session validate
		if (getCurrentUser() == null) {
			return false;
		}

		if (isUpdateMultitple()) { // not need to verify this case
			return true;
		}

		try {
			if (StringUtils.hasText(customBudget.getName())) {
				passed = false;
				PageUtils.logFailed("form_update", "Name is required");
			}

			if (customBudget.getActual() == 0) {
				passed = false;
				PageUtils.logFailed("form_update",
						"Actual amount must greater than zero");
			} else {
				if (isProjectedEnabled) {
					if (customBudget.getActual() <= customBudget.getProjected()) {
						passed = false;
						PageUtils.logFailed("form_update", "Actual must greater than projected");
					}
				}
			}

			if (customBudget.getScheduler().getBeginOn() == null) {
				passed = false;
				PageUtils.logFailed("form_update", "Start date is required!");
			}

			if (customBudget.getScheduler().getFinishOn() == null) {
				passed = false;
				PageUtils.logFailed("form_update", "End date is required!");
			}

			if (customBudget.getScheduler().getBeginOn() != null
					&& customBudget.getScheduler().getFinishOn() != null) {

				if (customBudget.getScheduler().getBeginOn().after(new Date())) {
					passed = false;
					PageUtils.logFailed("form_update",
							"Begin date must be after current date");
				} else if (customBudget.getScheduler().getBeginOn().after(customBudget.getScheduler().getFinishOn())) {
					passed = false;
					PageUtils.logFailed("form_update", "Finish date must after before date");
				}
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			log.error(ex);
			passed = false;
		}
		return passed;
	}

	public void initNewBudget() {
		customBudget = new Budget(getCurrentUser());

		// set new scheduler for budget
		Scheduler schedule = new Scheduler();
		schedule.setUserId(currentUser.getId());
		customBudget.setScheduler(schedule);
	}

	private void initChart() {
		budget_overview_model = new PieChartModel();
		budget_details_chart = new PieChartModel();

		budget_overview_model.set("", 0);
		budget_details_chart.set("", 0);
	}

	public void refreshBudgetOverviewChart() {
		budget_overview_model.clear();
		System.out.println("refreshBudget");
		if (bkBudgetTable != null && bkBudgetTable.size() > 0) {
			for (Budget budget : bkBudgetTable) {
				if (budget.isStatus()) {
					budget_overview_model.set(budget.getName(),
							budget.getActual());
				}
			}
		} else {
			budget_overview_model.set("", 0);
		}
	}

	public void refreshBudgetDetailsChart(ItemSelectEvent event) {
		budget_details_chart.clear();
		int userID = getCurrentUser().getId();
		try {

			budgetItemSelected = bkBudgetTable.get(event.getItemIndex());
			List<Object> trans = budgetService
					.getTransaction(budgetItemSelected, userID);

			setShowBudgetDetails(CollectionUtils.isEmpty(trans));

			if (!trans.isEmpty()) {
				for (int i = 0; i < trans.size(); i++) {
					Payment p = (Payment) trans.get(i);
					budget_details_chart.set(p.getName(), p.getAmount());
				}
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (budget_details_chart.getData().size() == 0) {
				budget_details_chart.set("", 0);
			}
		}

	}

	class LazyBudgetDataModel extends LazyDataModel<Budget> implements
			SelectableDataModel<Budget>, Serializable {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private ArrayList<Budget> dataSource;

		@SuppressWarnings("rawtypes")
		private List lstMeta;
		private int item_count = 0;

		// ---fileds
		public final String FIELD_NAME = "name";
		public final String FIELD_DESC = "description";
		public final String FILED_PROJECTED = "projected";
		public final String FIELD_ACTUAL = "actual";
		public final String FIELD_STATUS = "status";
		public boolean ignoreStatus = true;

		// -----------------------fields

		@Override
		public Budget getRowData(String rowKey) {
			for (int i = 0; i < dataSource.size(); i++) {
				if (dataSource.get(i).getId() == Converter.StringToInt(rowKey)) {
					return dataSource.get(i);
				}
			}
			return null;
		}

		@Override
		public Object getRowKey(Budget object) {
			return object.getId();
		}

		@SuppressWarnings({ "rawtypes", "unchecked" })
		@Override
		public List<Budget> load(int first, int pageSize, String sortField,
				SortOrder sortOrder, Map<String, String> filters) {
			System.out.println("load begin: " + System.currentTimeMillis());
			try {
				// generate example budget for query
				Budget exBudget = new Budget();

				try {
					if (filters.containsKey(FIELD_NAME)) {
						exBudget.setName(filters.get(FIELD_NAME));
					}
					if (filters.containsKey(FIELD_DESC)) {
						exBudget.setDescription(filters.get(FIELD_DESC));
					}
					if (filters.containsKey(FILED_PROJECTED)) {
						exBudget.setProjected(Converter.StringToFloat(filters.get(FILED_PROJECTED)));
					}
					if (filters.containsKey(FIELD_ACTUAL)) {
						exBudget.setProjected(Converter.StringToFloat(filters.get(FIELD_ACTUAL)));
					}
					if (filters.containsKey(FIELD_STATUS)) {
						int status = Converter.StringToInt(filters.get(FIELD_STATUS));
						if (status == -1) {
							ignoreStatus = true;
						} else {
							ignoreStatus = false;
							exBudget.setStatus(status == 1);
						}
					} else {
						ignoreStatus = true;
					}
				} catch (Exception ex) {
					PageUtils.logFailed("Enter wrong field type");
					setMetaData(null, 0);
					return null;
				}

				// ---done generate ex budget
				dataSource = (ArrayList<Budget>) budgetService.searchUser(first, pageSize, sortField, sortOrder, exBudget, ignoreStatus);
				System.out.println("datasource: " + dataSource.size());
				if (dataSource == null || dataSource.size() == 1) {
					setMetaData(null, 0);
					return null;
				}
				// get meta data
				lstMeta = (List) dataSource.get(0);
				item_count = ((Long) lstMeta.get(0)).intValue();

				// get user data
				dataSource = new ArrayList<Budget>(dataSource.subList(1, dataSource.size()));
				setMetaData(dataSource, item_count);
			} catch (Exception ex) {
				log.error(ex.getMessage(), ex);
			}
			System.out.println("load done: " + System.currentTimeMillis());
			return dataSource;
		}

		@SuppressWarnings({ "unchecked", "rawtypes" })
		public void setMetaData(List bkBudgetTable, int rowCount) {
			setRowCount(rowCount);
			BudgetBean.this.setBkBudgetTable(bkBudgetTable);
		}

		public int getItem_count() {
			return item_count;
		}

	}

	// ----------------------------------getter & setter
	public IBudgetService getBudgetService() {
		return budgetService;
	}

	public void setBudgetService(IBudgetService budgetService) {
		this.budgetService = budgetService;
	}

	public LazyDataModel<Budget> getLazyModel() {
		return lazyModel;
	}

	public List<Budget> getBkBudgetTable() {
		return bkBudgetTable;
	}

	public void setBkBudgetTable(List<Budget> bkBudgetTable) {
		this.bkBudgetTable = bkBudgetTable;
	}

	public Budget[] getSelectedRows() {
		return selectedRows;
	}

	public void setSelectedRows(Budget[] selectedRows) {
		this.selectedRows = selectedRows;
	}

	public Budget getCustomBudget() {
		return customBudget;
	}

	public void setCustomBudget(Budget customBudget) {
		this.customBudget = customBudget;
	}

	public boolean isUpdateMultitple() {
		return updateMultitple;
	}

	public void setUpdateMultitple(boolean updateMultitple) {
		this.updateMultitple = updateMultitple;
	}

	public int getIsDisabledAll() {
		return isDisabledAll;
	}

	public void setIsDisabledAll(int isDisabledAll) {
		this.isDisabledAll = isDisabledAll;
	}

	public boolean isProjectedEnabled() {
		return isProjectedEnabled;
	}

	public PieChartModel getBudget_overview_model() {
		return budget_overview_model;
	}

	public PieChartModel getBudget_details_chart() {
		return budget_details_chart;
	}

	public boolean isShowBudgetDetails() {
		return showBudgetDetails;
	}

	public void setShowBudgetDetails(boolean showBudgetDetails) {
		this.showBudgetDetails = showBudgetDetails;
	}

}
